#include "Avalon/Serialization/ShuttleDateTime.hpp"
#include <boost/date_time/gregorian/greg_serialize.hpp>

using namespace Avalon;
using namespace Avalon::Serialization;
using namespace boost;
using namespace std;

template<>
void Avalon::Serialization::Shuttle(DataShuttle* shuttle,
    posix_time::ptime::date_type& value, unsigned int version) {
  if(shuttle->IsReceiving()) {
    string dateString;
    shuttle->Shuttle("date", dateString);
    try {
      value = gregorian::from_undelimited_string(dateString);
    } catch(bad_lexical_cast&) {
      gregorian::special_values specialValue =
        gregorian::special_value_from_string(dateString);
      if(specialValue == gregorian::not_special) {
        BOOST_RETHROW;
      } else {
        value = gregorian::date(specialValue);
      }
    }
  } else {
    shuttle->Shuttle("date", to_iso_string(value));
  }
}

template<>
void Avalon::Serialization::Shuttle(DataShuttle* shuttle,
    posix_time::ptime::time_duration_type& value, unsigned int version) {
  if(shuttle->IsReceiving()) {
    bool isSpecial;
    shuttle->Shuttle("is_special", isSpecial);
    if(isSpecial) {
      string simpleString;
      shuttle->Shuttle("duration", simpleString);
      value = posix_time::time_duration(gregorian::special_value_from_string(
        simpleString));
    } else {
      posix_time::time_duration::hour_type hours;
      posix_time::time_duration::min_type minutes;
      posix_time::time_duration::sec_type seconds;
      posix_time::time_duration::fractional_seconds_type fractionalSeconds;
      shuttle->Shuttle("hours", hours);
      shuttle->Shuttle("minutes", minutes);
      shuttle->Shuttle("seconds", seconds);
      shuttle->Shuttle("fractional", fractionalSeconds);
      value = posix_time::time_duration(hours, minutes, seconds,
        fractionalSeconds);
    }
  } else {
    bool isSpecial = value.is_special();
    shuttle->Shuttle("is_special", isSpecial);
    if(isSpecial) {
      shuttle->Shuttle("duration", to_simple_string(value));
    } else {
      shuttle->Shuttle("hours", value.hours());
      shuttle->Shuttle("minutes", value.minutes());
      shuttle->Shuttle("seconds", value.seconds());
      shuttle->Shuttle("fractional", value.fractional_seconds());
    }
  }
}

template<>
void Avalon::Serialization::Shuttle(DataShuttle* shuttle,
    posix_time::ptime& value, unsigned int version) {
  if(shuttle->IsReceiving()) {
    posix_time::ptime::date_type date;
    shuttle->Shuttle("date", date);
    if(!date.is_special()) {
      posix_time::ptime::time_duration_type duration;
      shuttle->Shuttle("time", duration);
      value = boost::posix_time::ptime(date, duration);
    } else {
      value = boost::posix_time::ptime(date.as_special());
    }
  } else {
    shuttle->Shuttle("date", value.date());
    if(!value.is_special()) {
      shuttle->Shuttle("time", value.time_of_day());
    }
  }
}
